package org.hzero.gateway.helper.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import org.hzero.core.base.BaseHeaders;
import org.hzero.core.util.ServerRequestUtils;
import org.hzero.gateway.helper.api.HelperFilter;
import org.hzero.gateway.helper.entity.CheckState;
import org.hzero.gateway.helper.entity.PermissionDO;
import org.hzero.gateway.helper.entity.RequestContext;
import org.hzero.gateway.helper.service.PermissionService;
import org.hzero.starter.keyencrypt.core.IEncryptionService;

/**
 * 根据接口uri，method和service获取匹配到的权限
 * 匹配不到或者接口类型为内部接口，返回失败，不再向下执行
 */
@Component
public class GetPermissionFilter implements HelperFilter {

    private static final Logger LOGGER = LoggerFactory.getLogger(GetPermissionFilter.class);

    private final PermissionService permissionService;
    private final IEncryptionService encryptionService;

    public GetPermissionFilter(PermissionService permissionService, IEncryptionService encryptionService) {
        this.permissionService = permissionService;
        this.encryptionService = encryptionService;
    }

    @Override
    public int filterOrder() {
        return 20;
    }

    @Override
    public boolean shouldFilter(RequestContext context) {
        return true;
    }

    @Override
    public boolean run(RequestContext context) {
        // 记录本次请求的菜单ID
        context.setMenuId(getMenuId(context));

        String key = context.getRequestKey();
        // 通过spring 缓存来获取iam_permission 表中的权限信息,前提是要在 yml 中配置spring.cache.cache-names=permission
        // 否则还是走方法内的逻辑
        // 当前请求的信息中,通过服务名+方法名(get/post/put/delete)来过滤iam_permission的记录,将过滤结果中的path 来和uri来匹配
        PermissionDO permission = permissionService.selectPermissionByRequest(key);
        if (permission == null) {
            // 如果匹配不到,则出现403,并且不继续执行过滤器
            context.response.setStatus(CheckState.PERMISSION_MISMATCH);
            context.response.setMessage("This request mismatch any permission");
            return false;
        }
        // 为内部接口
        else if (permission.getWithin()) {
            // 如果获取到的记录中的within为 1 表示是内部接口,那么响应设置为402,并且不继续执行过滤器
            context.response.setStatus(CheckState.PERMISSION_WITH_IN);
            context.response.setMessage("No access to within interface");
            return false;
        } else {
            // 匹配到了,但是不是内部接口则记录下当前的权限信息并返回 true 继续执行过滤器
            context.setPermission(permission);
        }
        return true;
    }

    private Long getMenuId(RequestContext context) {
        Object servletRequest = context.getServletRequest();
        // 获取请求头上的 菜单 id=> H-Menu-Id
        String menuId = ServerRequestUtils.getHeaderValue(servletRequest, BaseHeaders.H_MENU_ID);

        if (StringUtils.isEmpty(menuId)) {
            return null;
        }

        Long id = null;
        try {
            // 如果 menuId 被加密过,则在此处进行解密操作
            if (encryptionService.isCipher(menuId)) {
                menuId = encryptionService.decrypt(menuId, "", context.request.accessToken);
            }
            id = Long.parseLong(menuId);
        } catch (NumberFormatException e) {
            LOGGER.warn("Header of [{}] format error, header value is [{}]", BaseHeaders.H_MENU_ID, menuId);
        }
        return id;
    }

}
